package keter.dao.base;

import java.util.List;

import keter.domain.base.BaseEntity;
import keter.util.ArrayUtil;
import keter.util.ClazzUtil;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

@Transactional
public abstract class KeterAbstractDao<E extends BaseEntity> implements KeterDao<E>{
		
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(KeterAbstractDao.class);

	private final Class<E> entityClass;
	
//	private final JpaEntityInformation<E, ?> entityInformation;

	@SuppressWarnings("unchecked")
	protected KeterAbstractDao() {
		this.entityClass = ClazzUtil.getSuperClassGenricType(this.getClass());
		SimpleJpaRepository sjp;
	}

	@Autowired
	private SessionFactory sessionFactory;
	
	/**
	 * <p>Method ：getCurrentSession
	 * <p>Description : session会在事务结束后自动关闭，因此方法必须事务中
	 * @return 
	 * @author  gulixing@msn.com
	 * @version 1.0.0
	 */
	public Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public E findOne(Long id) {
		Assert.notNull(id, "The given id must not be null!");
		return (E) getCurrentSession().get(entityClass, id);
	}

	@Override
	public void save(E e) {
		if(e.getId()==null){
			getCurrentSession().saveOrUpdate(e);					
		}
		else{
			getCurrentSession().merge(e);					
		}
	}
	
	public void saveList(Iterable<E> e) {
	        for (E entity : e) {
	            save(entity);
	        }
	}
	
	@Override
	public E saveEntity(E e) {
		if(isNew(e)){
			getCurrentSession().saveOrUpdate(e);					
		}
		else{
			getCurrentSession().merge(e);					
		}
		return e;
	}
	
	private boolean isNew(E e){
		return e.getId()==null?true:false;
	}
	
	@Override
	public void delete(E e) {
		Assert.notNull(e, "The entity must not be null!");
		getCurrentSession().delete(e);
	}

	@Override
	public void delete(Long id) {
		getCurrentSession().delete(findOne(id));
	}

	/**
	 * <p>
	 * Method ：findAll
	 * <p>
	 * Description : 功能描述
	 * 
	 * @return
	 * @see keter.dao.base.KeterDao#findAll()
	 */
	@Override
	public List<E> findAll() {
		return getCurrentSession().createCriteria(entityClass).list();
	}
	
	@Override
	public List<E> findAll(PageRequest pageable) {
		Criteria c = getCurrentSession().createCriteria(entityClass);
		c.setFirstResult(pageable.getPageNumber());
		c.setMaxResults(pageable.getPageSize());
		pageable.setCount(count());
		return c.list();
	}
	
	public long count() {
		String sqlQuery =  String.format(QueryTemplates.COUNT_QUERY_STRING, entityClass.getSimpleName());
		Query query = getCurrentSession().createQuery(sqlQuery);
		query.setMaxResults(2);// avoid OOME if the list is huge
		return (Long) query.list().get(0);
	}
	
	
	protected <T> T[] wrap(T... objects) {
		 return ArrayUtil.wrap(objects);
    }
	
	
	/**
	 * Executes query with given parameters and returns a list results.
	 * Parameters are passed in order of their posiion in parameters array.
	 * 
	 * @param sqlQueryQuery
	 *            query to execute
	 * @param parameters
	 *            parameters to pass to the query
	 * @return single result from query execution
	 */
	@SuppressWarnings("unchecked")
	protected List<E> getResultList(String sqlQuery, String[] names, Object[] parameters) {
		Query query = getCurrentSession().createQuery(sqlQuery);
		setQueryParameters(query, names, parameters);
		return (List<E>) query.list();
	}
	
	/**
	 * Executes query with given parameters and returns a single result.
	 * Parameters are passed along with given names.
	 * 
	 * @param sqlQuery
	 *            query to execute
	 * @param parameters
	 *            parameters to pass to the query
	 * @return single result from query execution
	 * 
	 */
	@SuppressWarnings("unchecked")
	protected E getSingleResult(String sqlQuery, String[] names, Object[] parameters) {
		Query query = getCurrentSession().createQuery(sqlQuery);
		query.setMaxResults(2);// avoid OOME if the list is huge
		setQueryParameters(query, names, parameters);
		List<E> result = (List<E>) query.list();
		return result.size() == 0 ? null : result.get(0);
	}

	/**
	 * Sets the parameters of a query. If names array is null, parameters are
	 * passed as positional parameters. Otherwise parameters are passed as named
	 * parameters, and position of parameter in array defines its name.
	 * 
	 * @param query
	 *            query instance
	 * @param names
	 *            names for corresponding parameters
	 * @param parameters
	 *            parameters passed to query
	 */
	protected void setQueryParameters(Query query, String[] names, Object[] parameters) {
		if (parameters != null) {
			if (names != null) {
				if (names.length != parameters.length) {
					throw new IllegalArgumentException(
							"numer of names is different from number of parameters");
				}

				for (int i = 0; i < parameters.length; i++) {
					query.setParameter(names[i], parameters[i]);
				}

			} else {
				for (int i = 0; i < parameters.length; i++) {
					query.setParameter(i, parameters[i]);
				}
			}
		}
	}
	
	
	@Override
	public void executeUpdate(String sqlQuery, String[] names, Object[] parameters) {
		Query query = getCurrentSession().createQuery(sqlQuery);
		query.setMaxResults(2);// avoid OOME if the list is huge
		setQueryParameters(query, names, parameters);
		query.executeUpdate();
	}
	
	
	@Override
	public void flush() {
		getCurrentSession().flush();
	}
}
